package top.guochenchen.mydb.backend.common;

import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import top.guochenchen.mydb.common.Error;

/**
 * AbstractCache 实现了一个引用计数策略的缓存
 * 这是 DM 模块的核心缓存框架，实现了引用计数策略的通用缓存。
 * 相比 LRU 缓存，引用计数缓存让上层模块能完全控制资源的驱逐时机，避免了缓存抖动问题。
 */
public abstract class AbstractCache<T> {                      // 抽象泛型类，T为缓存对象类型
    private HashMap<Long, T> cache;                           // 实际缓存的数据，key为资源ID，value为资源对象
    private HashMap<Long, Integer> references;                // 元素的引用个数，记录每个资源被引用的次数
    private HashMap<Long, Boolean> getting;                   // 正在获取某资源的线程标记，防止重复获取

    private int maxResource;                                  // 缓存的最大缓存资源数，0表示无限制
    private int count = 0;                                    // 缓存中元素的个数，用于容量控制
    private Lock lock;                                        // 并发控制锁，保护缓存操作的线程安全

    public AbstractCache(int maxResource) {                   // 构造函数，初始化缓存
        this.maxResource = maxResource;                       // 设置最大资源数
        cache = new HashMap<>();                              // 初始化缓存Map
        references = new HashMap<>();                         // 初始化引用计数Map
        getting = new HashMap<>();                            // 初始化获取标记Map
        lock = new ReentrantLock();                           // 创建可重入锁
    }

    /**
     * 获取流程详解：
     * 死循环等待：检查是否有其他线程正在获取该资源
     * 缓存命中：如果资源在缓存中，引用计数+1，直接返回
     * 缓存未满：检查缓存容量，未满则标记正在获取
     * 从数据源获取：调用抽象方法 getForCache(key)
     * 加入缓存：获取成功后加入缓存，引用计数设为1
     */
    protected T get(long key) throws Exception {              // 获取资源方法，key为资源标识
        while(true) {                                         // 死循环，直到成功获取资源
            lock.lock();                                      // 加锁，保护并发访问
            if(getting.containsKey(key)) {                    // 检查是否有其他线程正在获取该资源
                // 请求的资源正在被其他线程获取
                lock.unlock();                                // 释放锁
                try {
                    Thread.sleep(1);                          // 休眠1毫秒，避免忙等待
                } catch (InterruptedException e) {            // 处理中断异常
                    e.printStackTrace();                       // 打印异常堆栈
                    continue;                                 // 继续循环
                }
                continue;                                     // 继续循环，等待其他线程完成
            }

            if(cache.containsKey(key)) {                      // 检查资源是否在缓存中
                // 资源在缓存中，直接返回
                T obj = cache.get(key);                       // 从缓存中获取资源对象
                references.put(key, references.get(key) + 1); // 引用计数加1
                lock.unlock();                                // 释放锁
                return obj;                                   // 返回资源对象
            }

            // 尝试获取该资源
            if(maxResource > 0 && count == maxResource) {     // 检查缓存是否已满
                lock.unlock();                                // 释放锁
                throw Error.CacheFullException;               // 抛出缓存满异常
            }
            count ++;                                         // 缓存计数加1
            getting.put(key, true);                           // 标记该资源正在被获取
            lock.unlock();                                    // 释放锁
            break;                                            // 跳出循环，准备从数据源获取
        }

        T obj = null;                                         // 初始化资源对象
        try {
            obj = getForCache(key);                           // 调用抽象方法从数据源获取资源
        } catch(Exception e) {                                // 捕获获取异常
            lock.lock();                                      // 加锁
            count --;                                         // 缓存计数减1
            getting.remove(key);                              // 移除获取标记
            lock.unlock();                                    // 释放锁
            throw e;                                          // 重新抛出异常
        }

        lock.lock();                                          // 加锁
        getting.remove(key);                                  // 移除获取标记
        cache.put(key, obj);                                  // 将资源加入缓存
        references.put(key, 1);                               // 设置引用计数为1
        lock.unlock();                                        // 释放锁
        
        return obj;                                           // 返回资源对象
    }

    /**
     * 强行释放一个缓存，
     * 释放流程：
     * 引用计数减1
     * 如果引用计数归零，调用 releaseForCache() 写回数据源
     * 从缓存中移除该资源
     */
    protected void release(long key) {                        // 释放资源方法
        lock.lock();                                          // 加锁
        try {
            int ref = references.get(key)-1;                  // 引用计数减1
            if(ref == 0) {                                    // 如果引用计数归零
                T obj = cache.get(key);                       // 获取资源对象
                releaseForCache(obj);                         // 调用抽象方法写回数据源
                references.remove(key);                       // 移除引用计数记录
                cache.remove(key);                            // 从缓存中移除资源
                count --;                                     // 缓存计数减1
            } else {                                          // 如果引用计数不为零
                references.put(key, ref);                     // 更新引用计数
            }
        } finally {                                           // 确保锁被释放
            lock.unlock();                                    // 释放锁
        }
    }

    /**
     * 关闭缓存，写回所有资源
     */
    protected void close() {                                  // 关闭缓存方法
        lock.lock();                                          // 加锁
        try {
            Set<Long> keys = cache.keySet();                  // 获取所有缓存键
            for (long key : keys) {                           // 遍历所有键
                T obj = cache.get(key);                       // 获取资源对象
                releaseForCache(obj);                         // 写回数据源
                references.remove(key);                       // 移除引用计数
                cache.remove(key);                            // 从缓存中移除
            }
        } finally {                                           // 确保锁被释放
            lock.unlock();                                    // 释放锁
        }
    }


    /**
     * 当资源不在缓存时的获取行为
     */
    protected abstract T getForCache(long key) throws Exception; // 抽象方法：从数据源获取资源
    /**
     * 当资源被驱逐时的写回行为
     */
    protected abstract void releaseForCache(T obj);              // 抽象方法：将资源写回数据源
}